﻿using System;
using System.Windows.Forms;
using IBNet;
using System.Collections.Generic;
using System.Threading;
using System.Linq;
using IBNet.UtilsDataStructuresAndExtensions;

namespace IBNet.DayTrade
{
   interface IWrapDayTradeMainForm
   {
      /// <summary>
      /// Subscribes to five-second market data for specified contracts. Open-High-Low-Close-Volume data will arrive every five seconds via the IBClient.RealTimeBarReceived event.
      /// </summary>
      /// <param name="contracts">Contracts to subscribe to.</param>
      /// <param name="numberOfPriceBarsToStore">The number of price bars to store.</param>
      /// <param name="secondsPerPriceBar">The number of seconds of market data per complete Pricebar.</param>
      /// <param name="tradingHours">The trading hours. AllHours or RegularHours.</param>
      void SubscribeToRealTimeBars(IEnumerable<Contract> contracts,
         int numberOfPriceBarsToStore, RealTimeBarSeconds secondsPerPriceBar,
         TradingHours tradingHours);

      /// <summary>
      /// Subscribes to five-second market data for a specified Contract. Open-High-Low-Close-Volume data will arrive every five seconds via the IBClient.RealTimeBarReceived event.
      /// </summary>
      /// <param name="contract">A contract to subscribe to.</param>
      /// <param name="numberOfPriceBarsToStore">The number of price bars to store.</param>
      /// <param name="secondsPerPriceBar">The number of seconds of market data per complete Pricebar.</param>
      /// <param name="tradingHours">The trading hours. AllHours or RegularHours.</param>
      void SubscribeToRealTimeBars(Contract contract,
         int numberOfPriceBarsToStore, RealTimeBarSeconds secondsPerPriceBar,
         TradingHours tradingHours);

      /// <summary>
      /// Unsubscribes from a real time bar subscription.
      /// </summary>
      /// <param name="symbol">A symbol to unsubscribe from.</param>
      /// <param name="isSymbolNotFoundError">Specifies whether this unsubscribe action is the result of symbol-not-found error.</param>
      void UnsubscribeFromRealTimeBars(string fullSymbol, bool isSymbolNotFoundError);

      /// <summary>
      /// Unsubscribes from all real time bar subscriptions.
      /// </summary>
      void UnsubscribeFromAllRealTimeBars();

      /// <summary>
      /// Submits an order for a contract.
      /// </summary>
      /// <param name="orderAndContract">The OrderAndContract the order is for.</param>
      void SubmitOrder(Order order, Contract contract);

      /// <summary>
      /// Connects to Trader Workstation.
      /// </summary>
      /// <returns>true if successfully connected to Trader Workstation; otherwise, false.</returns>
      bool ConnectToTws();

      /// <summary>
      /// Disconnects from Trader Workstation.
      /// </summary>
      void DisconnectFromTws();
   }

   public partial class DayTradeMainForm : Form, IWrapDayTradeMainForm
   {
      void IWrapDayTradeMainForm.SubmitOrder(Order order, Contract contract)
      {
         OrderAndContract orderAndContract = new OrderAndContract(order, contract);
         lock (_onlySubmitStrictlyIncreasingOrderIdsLock)
         {
            orderAndContract.Order.OrderId = _currentOrderId++;
            switch (order.Flavor)
            {
            case OrderFlavor.Opening:
            {
               if (!_symbolsWithOpenDaytradeOrders.Contains(orderAndContract.FullSymbol))
               {
                  RegisterOrderAndContractForDayTrading(orderAndContract);
                  break;
               }
               else
               {
                  textBoxMessages.WriteLine("Error: Cannot trade ",
                     orderAndContract.FullSymbol, " until all open orders for ",
                     orderAndContract.FullSymbol, " are cancelled.");
                  return;
               }
            }
            case OrderFlavor.Bracket:
            case OrderFlavor.None:
            {
               break;
            }
            }
            ibClient.SubmitOrder(orderAndContract);
         }
      }

      void IWrapDayTradeMainForm.SubscribeToRealTimeBars(
         IEnumerable<Contract> contracts, int numberOfPriceBarsToStore, 
         RealTimeBarSeconds secondsPerPriceBar, TradingHours tradingHours)
      {
         foreach (Contract contract in contracts)
         {
            string fullSymbol = contract.FullSymbol;
            if (!listViewRealTimeBarSubscriptions.ContainsKey(fullSymbol))
            {
               RequestRealTimeBars(contract, numberOfPriceBarsToStore, 
                  secondsPerPriceBar, _defaultIndicators, tradingHours);
               
               // Sleep to comply with 50 outgoing messages / second limit.
               Thread.Sleep(20);
            }
            else
            {
               textBoxMessages.WriteLine("Error: Already subscribed to ", fullSymbol, ".");
            }
         }
      }

      void IWrapDayTradeMainForm.SubscribeToRealTimeBars(
         Contract contract, int numbersOfPriceBarsToStore, 
         RealTimeBarSeconds secondsPerPriceBar, TradingHours tradingHours)
      {
         IBClientWrapper.SubscribeToRealTimeBars(new Contract[] { contract },
            numbersOfPriceBarsToStore, secondsPerPriceBar, tradingHours);
      }

      void IWrapDayTradeMainForm.UnsubscribeFromRealTimeBars(string fullSymbol, 
         bool isSymbolNotFoundError)
      {
         int requestId = _symbolToUnsubscribeRequestId[fullSymbol];
         if (!isSymbolNotFoundError)
         // Then there is a real time bar subscription to cancel.
         // For symbol-not-found error, no subscription to real time bars
         // would have been made, so no call to ibClient.CancelRealTimeBars()
         // for when it is a symbol-not-found error.
         {
            ibClient.CancelRealTimeBars(requestId);
         }
         _symbolToUnsubscribeRequestId.Remove(fullSymbol);
         _requestIdToPriceBarsAndIndicatorValues.Remove(requestId);
         ibClient.UnregisterRealTimeBarRequestIdFromMultithreading(requestId);
         listViewRealTimeBarSubscriptions.Remove(fullSymbol, InvocationMethod.BeginInvoke);
      }

      void IWrapDayTradeMainForm.UnsubscribeFromAllRealTimeBars()
      {
         listViewRealTimeBarSubscriptions.InvokeIfNeeded(delegate
         {
            while (listViewRealTimeBarSubscriptions.Items.Count > 0)
            {
               string symbol = listViewRealTimeBarSubscriptions.Items[0].Text;
               IBClientWrapper.UnsubscribeFromRealTimeBars(symbol, false);
            }
         }, InvocationMethod.BeginInvoke);
      }

      /// <summary>
      /// Connects to Trader Workstation (TWS).
      /// </summary>
      /// <returns>true if successfully connected to TWS; otherwise, false.</returns>
      bool IWrapDayTradeMainForm.ConnectToTws()
      {
         if (Connected)
         {
            textBoxMessages.WriteLine("Already Connected.");
            return true;
         }
         if (_areDisconnected)
         {
            textBoxMessages.WriteLine("Error: Cannot reconnect once disconnected.");
            return false;
         }

         try
         {
            // 0 == clientId for DayTrade.exe
            // 1 == clientId for LongTermTrade.exe
            ibClient.ConnectToTws("127.0.0.1", 7496, 0);
            textBoxMessages.WriteLine("Connected at ", DateTime.Now.ToLongTimeString(), ".");
            return true;
         }
         catch (System.IO.IOException)
         // When user responds "No" to TWS connection prompt
         {
            textBoxMessages.WriteLine("Error: Could not connect to TWS.");
            return false;
         }
         catch (System.Net.Sockets.SocketException)
         // When running the program when TWS is not running 
         // or when running the program when not connected to the Internet
         {
            textBoxMessages.WriteLine("Error: Could not connect to TWS.");
            return false;
         }
      }

      void IWrapDayTradeMainForm.DisconnectFromTws()
      {
         if (!_areDisconnected)
         {
            ibClient.DisconnectFromTWS();
            textBoxMessages.WriteLine("Disconnected.");
            _areDisconnected = true;
         }
         else
         {
            textBoxMessages.WriteLine("Already disconnected.");
         }
      }
   }
}
